﻿/**
* @author	Matt Shaw 
* @url		http://sf.net/projects/xmlrpcflash
* 			http://www.osflash.org/doku.php?id=xmlrpcflash		
*
* @author   Daniel Mclaren (http://danielmclaren.net)
* @note     Updated to Actionscript 3.0
*
* @author   Ricardo 
* @note     Bug fixes for parameter values of zero or ''.
* @see      http://danielmclaren.net/2007/08/03/xmlrpc-for-actionscript-30-free-library#comment-327
*/

package com.mattism.http.xmlrpc
{
	import com.mattism.http.xmlrpc.util.XMLRPCDataTypes;
	import com.mattism.http.xmlrpc.util.XMLRPCUtils;
	
	import mx.formatters.DateFormatter;
	
	public class MethodCallImpl
	implements MethodCall
	{
		
		private var _VERSION:String = "1.0";
		private var _PRODUCT:String = "MethodCallImpl";
		private var _TRACE_LEVEL:Number = 3;	
		private var _parameters:Array;
		private var _name:String;
		private var _xml:XML;
		
		public function MethodCallImpl(){
			this.removeParams();
	
			this.debug("MethodCallImpl instance created. (v" + _VERSION + ")");
		}
		
		
		public function setName( name:String ):void {
			this._name=name;
		}
		
		public function addParam(params:Array):void {
			//this.debug("MethodCallImpl.addParam("+arguments+")");
			this._parameters = params;
		}
		
		public function removeParams():void {
			this._parameters=new Array();
		}
	
		public function getXml():XML {
			this.debug("getXml()");
			
			var ParentNode:XML;
			var ChildNode:XML;
			
			// Create the <methodCall>...</methodCall> root node
			ParentNode = <methodCall />;
			this._xml = ParentNode;
			
			// Create the <methodName>...</methodName> node
			ChildNode = <methodName>{this._name}</methodName>;
			ParentNode.appendChild(ChildNode);
			
			// Create the <params>...</params> node
			ChildNode = <params />;
			ParentNode.appendChild(ChildNode);
			ParentNode = ChildNode;
			
			// build nodes that hold all the params
			this.debug("Render(): Creating the params node.");
			
			var i:Number;		
			for (i=0; i<this._parameters.length; i++) {
				//this.debug("PARAM: " + [this._parameters[i].type,this._parameters[i].value]);
				ChildNode = <param />;
				ChildNode.appendChild( this.createParamsNode(this._parameters[i]) );
				ParentNode.appendChild(ChildNode);
			}
			this.debug("Render(): Resulting XML document:");
			this.debug("Render(): " + this._xml.toXMLString());
			trace("---------request----------------");
			trace(this._xml.toXMLString());
			
			return this._xml;
		}
		
		private function typeofParam (param:Object):String
		{
			var type:String = typeof(param);
			var myType:String = "";
			switch (type)
			{
				case "number":
					if(param is int)
					{
						myType = XMLRPCDataTypes.INT;
					}
					else
					{
						myType = XMLRPCDataTypes.DOUBLE;
					}
					break;
				case "string":
					myType =  XMLRPCDataTypes.STRING;
					break;
				case "boolean":
					myType =  XMLRPCDataTypes.BOOLEAN;
					break;
				case "object":
					if(param is Array)
					{
						myType = XMLRPCDataTypes.ARRAY;
					}
					else if(param is Date)
					{
						myType = XMLRPCDataTypes.DATETIME;
					}
					else
					{
						myType = XMLRPCDataTypes.STRUCT;
					}
					break;
			}
			return myType;
			
		}
			
		private function createParamsNode( parameter:Object ):XML {
			this.debug("CreateParameterNode()");
			var Node:XML = <value />;
			var TypeNode:XML;
			var v:Object;

			var type:String = typeofParam(parameter);
			

			// Handle Explicit Simple Objects
			if ( XMLRPCUtils.isSimpleType(type) ) {

				if(type == XMLRPCDataTypes.DATETIME)
				{
					TypeNode = <{type}>{getIso8601FromDate(parameter as Date)}</{type}>;
				}
				else
				{
					TypeNode = <{type}>{parameter}</{type}>;
				}				
				Node.appendChild(TypeNode);
				return Node;
			}
				// Handle Array Objects
			if (type == XMLRPCDataTypes.ARRAY) {
				var DataNode:XML;
				this.debug("CreateParameterNode(): >> Begin Array");
				TypeNode = <array />;
				DataNode = <data />;

				for (var i:int=0; i<parameter.length; i++) {
					DataNode.appendChild( this.createParamsNode( parameter[i] ) );
				}
				TypeNode.appendChild(DataNode);
				this.debug("CreateParameterNode(): << End Array");
				Node.appendChild(TypeNode);
				return Node;
			}
				// Handle Struct Objects
			if (type == XMLRPCDataTypes.STRUCT) {
				this.debug("CreateParameterNode(): >> Begin struct");
				TypeNode = <struct />;
				for (var x:String in parameter) {
					var MemberNode:XML = <member />;

					// add name node
					MemberNode.appendChild(<name>{x}</name>);

					// add value node
					var obj:* = parameter[x];
					if(XMLRPCUtils.isSimpleType(typeofParam(obj)))
					{
						if(typeofParam(obj) == XMLRPCDataTypes.DATETIME)
						{
							MemberNode.appendChild(<{type}>{getIso8601FromDate(parameter[x] as Date)}</{type}>);
						}
						else
						{
							MemberNode.appendChild(<value>{parameter[x]}</value>);
						}
					}
					else
					{
						MemberNode.appendChild(<value>{this.createParamsNode(obj)}</value>);
					}	
					TypeNode.appendChild(MemberNode);
				}
				this.debug("CreateParameterNode(): << End struct");
				Node.appendChild(TypeNode);
				return Node;
			}

			
			return Node;
		}
		public function getIso8601FromDate(date:Date):String
		{
			var formatter:DateFormatter = new DateFormatter();
			formatter.formatString = "YYYMMDDTHH:NN:SS";
			formatter.format(date);
			return formatter.format(date);
		}
		
		/*///////////////////////////////////////////////////////
		fixCDATAParameter()
		?:      Turns a cdata parameter into a string parameter with 
				CDATA wrapper
		IN:	    Possible CDATA parameter
		OUT:	Same parameter, CDATA'ed is necessary
		///////////////////////////////////////////////////////*/
		private function fixCDATAParameter(parameter:Object):Object{
			if (parameter.type==XMLRPCDataTypes.CDATA){
				parameter.type=XMLRPCDataTypes.STRING;
				parameter.value='<![CDATA['+parameter.value+']]>';  
			}
			return parameter;
		}

		
		public function cleanUp():void {
			//this.removeParams();
			//this.parseXML(null);
		}
	
		private function debug(a:Object):void {
			//trace(a);
		}
	}
}